package control;

import model.interfaces.commandTypes.DirectionArgs;
import model.interfaces.commandTypes.MoveArgs;
import model.interfaces.commandTypes.NoArgs;
import model.interfaces.commandTypes.ArmyArgs;
import java.awt.event.KeyEvent;

import model.interfaces.selectable.ControllableModel;
import view.View;

/**
 * Global hotkey manager. Responsible for forwarding key requests, game ticks,
 * etc. to the other areas' key managers, as well as responding to the global
 * hotkeys.
 * 
 * @author Ross Nichols
 */

public class Control extends GameKeyAdapter
{
    private ControllableModel model;
    
    private MainAreaKeyAdapter ma;
    private UnitOverviewKeyAdapter uo;
    private StructureOverviewKeyAdapter so;
    private GameKeyAdapter[] adapters;
    private boolean gameActive = false;
    
    // constants
    final private int UNIT_OVERVIEW = KeyEvent.VK_O;
    final private int STRUCTURE_OVERVIEW = KeyEvent.VK_K;
    final private int GAME_TOGGLE = KeyEvent.VK_S;
    final private int CLOSE_OVERVIEW = KeyEvent.VK_ESCAPE;
    final private long DELAY_LENGTH = 150; // in milliseconds
    
    /**
     * When the Control is created, it is immediately actived.
     */
    public Control() {
        activate();
        // System.out.println( "test..." );
    }
    
    protected void doActivation()
    {
        // only happens on startup; do not need extra info
    }
    
    /**
     * The Control cannot be deactivated.
     */
    protected void doDeactivation()
    {
        throw new RuntimeException( "The global controller should never be disabled!" );
    }
    
    /**
     * Called by the Model to inform the Control of the existence of the Model
     * and the View. Creates the other key adapters.
     */
    public void register( ControllableModel m, View v )
    {
        model = m;
        
        ma = new MainAreaKeyAdapter( m, v );
        uo = new UnitOverviewKeyAdapter( m, v );
        so = new StructureOverviewKeyAdapter( m, v );
        
        adapters = new GameKeyAdapter[]
        {
            ma, uo, so
        };
    }
    
    /**
     * Handles the global hotkeys and forwards the remaining events to the
     * active managers.
     * 
     * @param e
     *            the KeyEvent
     */
    public void keyPressed( KeyEvent e )
    {
        if ( !handle( e ) )
        {
            for ( GameKeyAdapter a : adapters )
            {
                if ( a.activated() ) a.keyPressed( e );
            }
        }
    }
    
    /**
     * Handles the global hotkeys.
     * 
     * @param e
     *            the KeyEvent
     * @return if the input was handled here
     **/
    private boolean handle( KeyEvent e )
    {
        if ( suppress( e ) ) return true;
        
        if ( e.getKeyCode() == GAME_TOGGLE )
        {
            if ( gameActive )
            {
                model.endGame();
                gameActive = false;
            }
            else
            {
                model.startGame();
                gameActive = true;
                ma.activate();
            }
            
            return true;
        }
        // the only active hotkey when the game isn't active is the game toggle
        else if ( gameActive )
        {
            if ( e.getKeyCode() == UNIT_OVERVIEW )
            {
                for ( GameKeyAdapter a : adapters )
                {
                    if ( a != uo ) a.deactivate();
                }
                uo.activate();
                
                return true;
            }
            else if ( e.getKeyCode() == STRUCTURE_OVERVIEW )
            {
                for ( GameKeyAdapter a : adapters )
                {
                    if ( a != so ) a.deactivate();
                }
                so.activate();
                
                return true;
            }
            else if ( e.getKeyCode() == CLOSE_OVERVIEW )
            {
                if ( !ma.activated() )
                {
                    for ( GameKeyAdapter a : adapters )
                    {
                        if ( a != ma ) a.deactivate();
                    }
                    ma.activate();
                    
                    return true;
                }
            }
        }
        
        return false;
    }
    
    /**
     * Slows down the rate at which KeyEvents repeat. Ignores duplicate (by key
     * code) events that occur less than DELAY_TIME apart
     * 
     * @param e
     *            the KeyEvent that may be thrown away
     * @return whether the event should be ignored
     */
    private KeyEvent old = null;
    private long prevTime = 0;
    
    private boolean suppress( KeyEvent e )
    {
        long time = System.currentTimeMillis();
        if ( old == null || e.getKeyCode() != old.getKeyCode() || time - prevTime > DELAY_LENGTH )
        {
            old = e;
            prevTime = time;
            return false;
        }
        
        return true;
    }
    
    /**
     * Called by the Model on game ticks
     */
    public void onTick()
    {
        for ( GameKeyAdapter a : adapters )
        {
            if ( a.activated() )
            {
                a.onTick();
            }
        }
    }
    
    /**
     * Called by the Model on game begin
     */
    public void onGameBegin()
    {
        System.out.println( "game begin!" );
        gameActive = true;
        for ( GameKeyAdapter a : adapters )
        {
            if ( a != ma ) a.deactivate();
        }
        ma.activate();
    }
    
    /**
     * Called by the Model on game loss
     */
    public void onGameLose()
    {
        gameActive = false;
        for ( GameKeyAdapter a : adapters )
        {
            a.deactivate();
        }
        System.out.println( "game lose!" );
    }
    
    /**
     * Called by the Model on game win
     */
    public void onGameWin()
    {
        gameActive = false;
        for ( GameKeyAdapter a : adapters )
        {
            a.deactivate();
        }
        System.out.println( "game win!" );
    }
    
    public void visitMoveArgs( MoveArgs args )
    {
        // not used
    }
    
    public void visitArmyArgs( ArmyArgs args )
    {
        // not used
    }
    
    public void visitDirectionArgs( DirectionArgs args )
    {
        // not used
    }
    
    public void visitNoArgs( NoArgs args )
    {
        // not used
    }
}
